﻿// Programmatic T4 Template rendering derived from Eric Hexter's great work on Flywheel - http://flywheel.codeplex.com/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using dotNails.CodeGen.Models;
using System.IO;
using Microsoft.VisualStudio.TextTemplating;
using System.Reflection;
using System.CodeDom.Compiler;

namespace dotNails.CodeGen.T4 {
	public class T4Template {
		public string TemplateFileName { get; set; }
		protected Table Table { get; set; }
		private string _templatePath = string.Empty;
		public string TemplatePath {
			get {
				if (string.IsNullOrEmpty(_templatePath)) {
					_templatePath = Microsoft.Win32.Registry.GetValue(@"HKEY_LOCAL_MACHINE\Software\dotNails", "TemplatePath", string.Empty).ToString();
					if (string.IsNullOrEmpty(_templatePath)) {
						throw new Exception(@"TemplatePath not found or is null in registry: HKEY_LOCAL_MACHINE\Software\dotNails\TemplatePath");
					}
				}
				return _templatePath;
			}
			set {
				_templatePath = value;
			}
		}

		#region Constructors
		public T4Template(Table table) {
			Table = table;
		}
		#endregion Constructors
		
		public string Render(out TemplateRunResult result) {
			string output = string.Empty;
			string templateContent = string.Empty;
			string templateFilePath = Path.Combine(TemplatePath, TemplateFileName);

			// Open the file to read from.
			if (File.Exists(templateFilePath)) {
				using (StreamReader sr = File.OpenText(templateFilePath)) {
					templateContent = sr.ReadToEnd();
				}
			}
			else {
				throw new Exception(string.Format("RunTemplate failed - templateFilePath: \"{0}\" does not exist", templateFilePath));
			}
			templateContent = templateContent + AddAllTemplateProperties();

			ITextTemplatingEngine engine = new Engine();
			TextTemplateHost host = CreateTextTemplateHost(Table);
			host.TemplateFile = templateFilePath;
			output = engine.ProcessTemplate(templateContent, host);

			result = CreateEmptyResult(templateFilePath);
			CollectErrorsAndWarningIntoTheResult(host, result);

			if (!host.Errors.HasErrors) {
				result.Result = RunResult.Success;
			}
			
			return output;
		}

		private TextTemplateHost CreateTextTemplateHost(Table table) {
			var host = new TextTemplateHost();
			host.Table = table;
			host.AssemblyPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
			return host;
		}

		private TemplateRunResult CreateEmptyResult(string templateFilePath) {
			return new TemplateRunResult {
				Errors = new List<T4Error>(),
				Warnings = new List<T4Error>(),
				Result = RunResult.NotRun,
				TemplateFilePath = templateFilePath
			};
		}

		private void CollectErrorsAndWarningIntoTheResult(TextTemplateHost host, TemplateRunResult result) {
			if (host.Errors.HasWarnings) {
				result.Result = RunResult.Warning;
				foreach (CompilerError error in host.Errors) {
					if (error.IsWarning) {
						result.Warnings.Add(new T4Error { Column = error.Column, Description = error.ErrorText, Line = error.Line });
					}
				}
			}

			if (host.Errors.HasErrors) {
				result.Result = RunResult.Error;
				foreach (CompilerError error in host.Errors) {
					if (!error.IsWarning) {
						result.Errors.Add(new T4Error { Column = error.Column, Description = error.ErrorText, Line = error.Line });
					}
				}
			}
		}

		protected virtual string AddTemplateProperties() {
			return string.Empty;
		}

		protected string AddAllTemplateProperties() {
			string allProperties = @"
<#+" + AddCoreTemplateProperties() + AddTemplateProperties() + @"
#>";
			return allProperties;
		}

		public static string AddCoreTemplateProperties() {
			return @"
	public dotNails.CodeGen.Models.Table Table = ((ITemplateContext)System.AppDomain.CurrentDomain.GetData(""host"")).Table;";
		}
	}
}
